Entity Framework 的 Find 與 Single 的選擇
TLDR
Find()會優先搜尋本地快取(Local Cache),若找不到才會發送資料庫查詢。Find()不支援Include()關聯預加載,且無法與AsNoTracking()搭配使用。Single()與SingleOrDefault()語意明確,適合需要進行複雜查詢或關聯資料載入的場景。- 若需確保資料存在,建議使用
Single();若允許找不到資料,則使用SingleOrDefault()。 Find()的參數為object[],在編譯階段無法檢查型別正確性,使用時需特別注意主鍵順序。
Find() 方法的特性與限制
什麼情況下會遇到這個問題:當開發者需要透過主鍵取得單一實體,且希望減少不必要的資料庫往返時。
Find() 方法的定義如下:
csharp
public virtual TEntity? Find (params object?[]? keyValues);其核心機制與注意事項如下:
- 優先查詢本地快取:
Find()會先檢查DbContext的本地快取(包含已載入、已查詢或已新增的 Entity),若命中則直接回傳,不會發送 SQL 查詢。 - 資料庫查詢:若本地快取中不存在該 Entity,才會向資料庫發送查詢請求。
- 型別安全性:由於參數為
object[],編譯器無法檢查輸入型別。處理複合主鍵時,必須嚴格遵守ColumnAttribute或 Fluent API 定義的順序,否則會導致查詢失敗。 - 功能限制:
- 不支援
Include()進行關聯資料預加載(Eager Loading)。 - 若
DbSet使用了AsNoTracking(),則無法使用Find()方法。 - 若查詢結果未被追蹤(如使用
AsNoTracking()查詢),該結果也不會進入本地快取。
- 不支援
Find() 與 Single() 的語意與選擇
什麼情況下會遇到這個問題:當開發者在評估使用 Find() 或 Single() 來取得單一資料時。
在 Entity Framework 中,方法命名通常隱含了行為模式:
- Find 系列:通常在找不到資料時回傳
null(或default)。 - Get 系列:通常在找不到資料時拋出異常。
雖然 Find() 在效能上因快取機制可能較快,但 Single() 與 SingleOrDefault() 提供了更強大的查詢彈性:
- 語意明確:
Single()在找不到資料或找到多筆時會拋出異常,適合預期資料「一定存在」的邏輯;SingleOrDefault()則在找不到時回傳null。 - 查詢彈性:
Single()支援Include()關聯載入,且能搭配AsNoTracking()使用,適用於更複雜的業務邏輯。
結論
建議優先使用 Single() 或 SingleOrDefault(),因為它們在語意上更為明確,且不受 Find() 的快取機制與功能限制影響。僅在明確需要利用本地快取以提升效能,且不需要關聯資料載入的簡單主鍵查詢情境下,才考慮使用 Find()。
異動歷程
- 初版文件建立。